package com.netstar.property;

import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;

public abstract class Property implements Serializable {

	private static final long serialVersionUID = 2707559104317898946L;

	/**
	 * A default value for a integer field which the server may not set.
	 */
	public static final int DEFAULT_UNSET_FIELD = -1;

	private static HashMap<Class<? extends Property>, PropertyClass> sSubclassMap;
	private static HashMap<String, PropertyClass> sNameMap;

	private static void registerSubclass(PropertyClass klass) {
		sSubclassMap.put(klass.objectClass, klass);
		if (null != klass.className) {
			sNameMap.put(klass.className, klass);
		}
	}

	public static PropertyClass getKlass(Class<?> subclass) {
		return sSubclassMap.get(subclass);
	}

	public static PropertyClass getKlass(String klassName) {
		return sNameMap.get(klassName);
	}

	public static PropertyClass getPropertyClass() {
		PropertyClass klass = new PropertyClass(Property.class, null) {
			public Property factory() {
				return null;
			}
		};
		return klass;
	}

	public final void shallowCopy(Property otherResource) {
		if (otherResource.getClass() != getClass()) {
			throw new UnsupportedOperationException(
					"You can only shallowCopy the same type of resource");
		}

		unguardedShallowCopy(otherResource);
	}

	public final void shallowCopyAncestorType(Property otherResource) {
		Class<?> c = getClass();
		final Class<?> o = otherResource.getClass();
		if (o != Property.class) {
			while (c != o && c != Property.class) {
				c = c.getSuperclass();
			}
			if (c == Property.class) {
				throw new UnsupportedOperationException(o.getName()
						+ " is not a superclass of " + getClass().getName());
			}
		}

		unguardedShallowCopy(otherResource);
	}

	private final void unguardedShallowCopy(Property otherResource) {
		PropertyClass rc = getKlass(otherResource.getClass());
		for (Map.Entry<String, AbstractProperty> e : rc.properties.entrySet()) {
			AbstractProperty rp = e.getValue();
			if (rp instanceof PrimitiveProperty) {
				((PrimitiveProperty) rp).copy(this, otherResource);
			} else if (rp instanceof ArrayProperty) {
				((ArrayProperty) rp).set(this,
						((ArrayProperty) rp).get(otherResource));
			} else if (rp instanceof NestedProperty) {
				((NestedProperty) rp).set(this,
						((NestedProperty) rp).get(otherResource));
			}
		}
	}

	static {
		// Register all subclasses
		sSubclassMap = new HashMap<Class<? extends Property>, PropertyClass>();
		sNameMap = new HashMap<String, PropertyClass>();

		registerSubclass(Property.getPropertyClass());
	}
}
